{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "06432a41-540a-4a55-b3fa-8665032e6127",
   "metadata": {},
   "source": [
    "## Runnable Interface 介绍与使用\n",
    "\n",
    "为了尽可能简化创建自定义链的过程，Langchain 实现了一个 **[Runnable](https://api.python.langchain.com/en/stable/runnables/langchain_core.runnables.base.Runnable.html#langchain_core.runnables.base.Runnable)** 协议。\n",
    "\n",
    "许多 LangChain 组件都实现了 Runnable 协议，包括 chat models, LLMs, output parsers, retrievers, prompt templates等等。此外，还有几个用于处理可运行对象的[有用原语](https://python.langchain.com/v0.1/docs/expression_language/primitives/)。\n",
    "\n",
    "Runnable 是一个标准接口，包括：\n",
    "\n",
    "- stream：流式返回生成内容（chunk）\n",
    "- invoke：对输入调用该链\n",
    "- batch：对输入列表调用该链\n",
    "\n",
    "不同组件的输入和输出类型有所差异:\n",
    "\n",
    "| 组件    | 输入类型                                           | 输出类型           |\n",
    "| ------------ | ----------------------------------------------------- | --------------------- |\n",
    "| Prompt       | Dictionary                                            | PromptValue           |\n",
    "| ChatModel    | Single string, list of chat messages or a PromptValue | ChatMessage           |\n",
    "| LLM          | Single string, list of chat messages or a PromptValue | String                |\n",
    "| OutputParser | The output of an LLM or ChatModel                     | Depends on the parser |\n",
    "| Retriever    | Single string                                         | List of Documents     |\n",
    "| Tool         | Single string or dictionary, depending on the tool    | Depends on the tool   |\n",
    "\n",
    "\n",
    "所有 Runnable 对象都显式描述输入和输出 Schema，以检查输入和输出格式：\n",
    "\n",
    "- input_schema：从Runnable的结构自动生成的输入Pydantic模型\n",
    "- output_schema：从Runnable的结构自动生成的输出Pydantic模型 \n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f03529a6-80f3-425a-988c-0f1b0dd17d3c",
   "metadata": {},
   "source": [
    "### Input Schema\n",
    "\n",
    "为了演示如何使用，下面我们创建一个超级简单的PromptTemplate + ChatModel链。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "69a3f4fd-e4dc-45ac-a9cf-a36b5791aafc",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_openai import ChatOpenAI\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "\n",
    "model = ChatOpenAI(model=\"gpt-3.5-turbo-0125\")\n",
    "prompt = ChatPromptTemplate.from_template(\"讲个关于 {topic} 的笑话吧\")\n",
    "chain = prompt | model"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9e5bb871-a240-429b-b622-23a65f5b4af2",
   "metadata": {},
   "source": [
    "#### schema 方法\n",
    "\n",
    "一个描述 Runnable 接受的输入的说明。这是根据任何 Runnable 结构动态生成的 Pydantic 模型。您可以调用 .schema() 来获取 `JSONSchema` 表示。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "15a39360-11de-407f-92bc-c372a5a8efb4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'title': 'PromptInput',\n",
       " 'type': 'object',\n",
       " 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}}"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 查看 Chain 的输入类型\n",
    "chain.input_schema.schema()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "2b8fc874-0a36-404a-a627-72dfd50d719b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'title': 'PromptInput',\n",
       " 'type': 'object',\n",
       " 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}}"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 查看 Prompt 的输入类型（Chain的输入从 Prompt 开始，因此输入类型一致）\n",
    "prompt.input_schema.schema()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "3a6077c1-4add-4d0a-a81b-b4b8dba1d950",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'title': 'ChatOpenAIInput',\n",
       " 'anyOf': [{'type': 'string'},\n",
       "  {'$ref': '#/definitions/StringPromptValue'},\n",
       "  {'$ref': '#/definitions/ChatPromptValueConcrete'},\n",
       "  {'type': 'array',\n",
       "   'items': {'anyOf': [{'$ref': '#/definitions/AIMessage'},\n",
       "     {'$ref': '#/definitions/HumanMessage'},\n",
       "     {'$ref': '#/definitions/ChatMessage'},\n",
       "     {'$ref': '#/definitions/SystemMessage'},\n",
       "     {'$ref': '#/definitions/FunctionMessage'},\n",
       "     {'$ref': '#/definitions/ToolMessage'}]}}],\n",
       " 'definitions': {'StringPromptValue': {'title': 'StringPromptValue',\n",
       "   'description': 'String prompt value.',\n",
       "   'type': 'object',\n",
       "   'properties': {'text': {'title': 'Text', 'type': 'string'},\n",
       "    'type': {'title': 'Type',\n",
       "     'default': 'StringPromptValue',\n",
       "     'enum': ['StringPromptValue'],\n",
       "     'type': 'string'}},\n",
       "   'required': ['text']},\n",
       "  'AIMessage': {'title': 'AIMessage',\n",
       "   'description': 'Message from an AI.',\n",
       "   'type': 'object',\n",
       "   'properties': {'content': {'title': 'Content',\n",
       "     'anyOf': [{'type': 'string'},\n",
       "      {'type': 'array',\n",
       "       'items': {'anyOf': [{'type': 'string'}, {'type': 'object'}]}}]},\n",
       "    'additional_kwargs': {'title': 'Additional Kwargs', 'type': 'object'},\n",
       "    'response_metadata': {'title': 'Response Metadata', 'type': 'object'},\n",
       "    'type': {'title': 'Type',\n",
       "     'default': 'ai',\n",
       "     'enum': ['ai'],\n",
       "     'type': 'string'},\n",
       "    'name': {'title': 'Name', 'type': 'string'},\n",
       "    'id': {'title': 'Id', 'type': 'string'},\n",
       "    'example': {'title': 'Example', 'default': False, 'type': 'boolean'}},\n",
       "   'required': ['content']},\n",
       "  'HumanMessage': {'title': 'HumanMessage',\n",
       "   'description': 'Message from a human.',\n",
       "   'type': 'object',\n",
       "   'properties': {'content': {'title': 'Content',\n",
       "     'anyOf': [{'type': 'string'},\n",
       "      {'type': 'array',\n",
       "       'items': {'anyOf': [{'type': 'string'}, {'type': 'object'}]}}]},\n",
       "    'additional_kwargs': {'title': 'Additional Kwargs', 'type': 'object'},\n",
       "    'response_metadata': {'title': 'Response Metadata', 'type': 'object'},\n",
       "    'type': {'title': 'Type',\n",
       "     'default': 'human',\n",
       "     'enum': ['human'],\n",
       "     'type': 'string'},\n",
       "    'name': {'title': 'Name', 'type': 'string'},\n",
       "    'id': {'title': 'Id', 'type': 'string'},\n",
       "    'example': {'title': 'Example', 'default': False, 'type': 'boolean'}},\n",
       "   'required': ['content']},\n",
       "  'ChatMessage': {'title': 'ChatMessage',\n",
       "   'description': 'Message that can be assigned an arbitrary speaker (i.e. role).',\n",
       "   'type': 'object',\n",
       "   'properties': {'content': {'title': 'Content',\n",
       "     'anyOf': [{'type': 'string'},\n",
       "      {'type': 'array',\n",
       "       'items': {'anyOf': [{'type': 'string'}, {'type': 'object'}]}}]},\n",
       "    'additional_kwargs': {'title': 'Additional Kwargs', 'type': 'object'},\n",
       "    'response_metadata': {'title': 'Response Metadata', 'type': 'object'},\n",
       "    'type': {'title': 'Type',\n",
       "     'default': 'chat',\n",
       "     'enum': ['chat'],\n",
       "     'type': 'string'},\n",
       "    'name': {'title': 'Name', 'type': 'string'},\n",
       "    'id': {'title': 'Id', 'type': 'string'},\n",
       "    'role': {'title': 'Role', 'type': 'string'}},\n",
       "   'required': ['content', 'role']},\n",
       "  'SystemMessage': {'title': 'SystemMessage',\n",
       "   'description': 'Message for priming AI behavior, usually passed in as the first of a sequence\\nof input messages.',\n",
       "   'type': 'object',\n",
       "   'properties': {'content': {'title': 'Content',\n",
       "     'anyOf': [{'type': 'string'},\n",
       "      {'type': 'array',\n",
       "       'items': {'anyOf': [{'type': 'string'}, {'type': 'object'}]}}]},\n",
       "    'additional_kwargs': {'title': 'Additional Kwargs', 'type': 'object'},\n",
       "    'response_metadata': {'title': 'Response Metadata', 'type': 'object'},\n",
       "    'type': {'title': 'Type',\n",
       "     'default': 'system',\n",
       "     'enum': ['system'],\n",
       "     'type': 'string'},\n",
       "    'name': {'title': 'Name', 'type': 'string'},\n",
       "    'id': {'title': 'Id', 'type': 'string'}},\n",
       "   'required': ['content']},\n",
       "  'FunctionMessage': {'title': 'FunctionMessage',\n",
       "   'description': 'Message for passing the result of executing a function back to a model.',\n",
       "   'type': 'object',\n",
       "   'properties': {'content': {'title': 'Content',\n",
       "     'anyOf': [{'type': 'string'},\n",
       "      {'type': 'array',\n",
       "       'items': {'anyOf': [{'type': 'string'}, {'type': 'object'}]}}]},\n",
       "    'additional_kwargs': {'title': 'Additional Kwargs', 'type': 'object'},\n",
       "    'response_metadata': {'title': 'Response Metadata', 'type': 'object'},\n",
       "    'type': {'title': 'Type',\n",
       "     'default': 'function',\n",
       "     'enum': ['function'],\n",
       "     'type': 'string'},\n",
       "    'name': {'title': 'Name', 'type': 'string'},\n",
       "    'id': {'title': 'Id', 'type': 'string'}},\n",
       "   'required': ['content', 'name']},\n",
       "  'ToolMessage': {'title': 'ToolMessage',\n",
       "   'description': 'Message for passing the result of executing a tool back to a model.',\n",
       "   'type': 'object',\n",
       "   'properties': {'content': {'title': 'Content',\n",
       "     'anyOf': [{'type': 'string'},\n",
       "      {'type': 'array',\n",
       "       'items': {'anyOf': [{'type': 'string'}, {'type': 'object'}]}}]},\n",
       "    'additional_kwargs': {'title': 'Additional Kwargs', 'type': 'object'},\n",
       "    'response_metadata': {'title': 'Response Metadata', 'type': 'object'},\n",
       "    'type': {'title': 'Type',\n",
       "     'default': 'tool',\n",
       "     'enum': ['tool'],\n",
       "     'type': 'string'},\n",
       "    'name': {'title': 'Name', 'type': 'string'},\n",
       "    'id': {'title': 'Id', 'type': 'string'},\n",
       "    'tool_call_id': {'title': 'Tool Call Id', 'type': 'string'}},\n",
       "   'required': ['content', 'tool_call_id']},\n",
       "  'ChatPromptValueConcrete': {'title': 'ChatPromptValueConcrete',\n",
       "   'description': 'Chat prompt value which explicitly lists out the message types it accepts.\\nFor use in external schemas.',\n",
       "   'type': 'object',\n",
       "   'properties': {'messages': {'title': 'Messages',\n",
       "     'type': 'array',\n",
       "     'items': {'anyOf': [{'$ref': '#/definitions/AIMessage'},\n",
       "       {'$ref': '#/definitions/HumanMessage'},\n",
       "       {'$ref': '#/definitions/ChatMessage'},\n",
       "       {'$ref': '#/definitions/SystemMessage'},\n",
       "       {'$ref': '#/definitions/FunctionMessage'},\n",
       "       {'$ref': '#/definitions/ToolMessage'}]}},\n",
       "    'type': {'title': 'Type',\n",
       "     'default': 'ChatPromptValueConcrete',\n",
       "     'enum': ['ChatPromptValueConcrete'],\n",
       "     'type': 'string'}},\n",
       "   'required': ['messages']}}}"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 查看 Model 的输入类型\n",
    "model.input_schema.schema()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e7914531-4e20-4d62-b95d-72930ad6f3f0",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ff6d4c11-31b5-4edc-9a4b-611644aea24d",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "db2bc6e0-0e6e-497d-bde3-3f24fa9ded77",
   "metadata": {},
   "source": [
    "### Output Schema\n",
    "\n",
    "输出类型仍然可以调用 .schema() 来获取其 `JSONSchema` 表示。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "0bc07c2a-f9fb-4013-a7c3-6a74c2cb3754",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'title': 'ChatOpenAIOutput',\n",
       " 'anyOf': [{'$ref': '#/definitions/AIMessage'},\n",
       "  {'$ref': '#/definitions/HumanMessage'},\n",
       "  {'$ref': '#/definitions/ChatMessage'},\n",
       "  {'$ref': '#/definitions/SystemMessage'},\n",
       "  {'$ref': '#/definitions/FunctionMessage'},\n",
       "  {'$ref': '#/definitions/ToolMessage'}],\n",
       " 'definitions': {'AIMessage': {'title': 'AIMessage',\n",
       "   'description': 'Message from an AI.',\n",
       "   'type': 'object',\n",
       "   'properties': {'content': {'title': 'Content',\n",
       "     'anyOf': [{'type': 'string'},\n",
       "      {'type': 'array',\n",
       "       'items': {'anyOf': [{'type': 'string'}, {'type': 'object'}]}}]},\n",
       "    'additional_kwargs': {'title': 'Additional Kwargs', 'type': 'object'},\n",
       "    'response_metadata': {'title': 'Response Metadata', 'type': 'object'},\n",
       "    'type': {'title': 'Type',\n",
       "     'default': 'ai',\n",
       "     'enum': ['ai'],\n",
       "     'type': 'string'},\n",
       "    'name': {'title': 'Name', 'type': 'string'},\n",
       "    'id': {'title': 'Id', 'type': 'string'},\n",
       "    'example': {'title': 'Example', 'default': False, 'type': 'boolean'}},\n",
       "   'required': ['content']},\n",
       "  'HumanMessage': {'title': 'HumanMessage',\n",
       "   'description': 'Message from a human.',\n",
       "   'type': 'object',\n",
       "   'properties': {'content': {'title': 'Content',\n",
       "     'anyOf': [{'type': 'string'},\n",
       "      {'type': 'array',\n",
       "       'items': {'anyOf': [{'type': 'string'}, {'type': 'object'}]}}]},\n",
       "    'additional_kwargs': {'title': 'Additional Kwargs', 'type': 'object'},\n",
       "    'response_metadata': {'title': 'Response Metadata', 'type': 'object'},\n",
       "    'type': {'title': 'Type',\n",
       "     'default': 'human',\n",
       "     'enum': ['human'],\n",
       "     'type': 'string'},\n",
       "    'name': {'title': 'Name', 'type': 'string'},\n",
       "    'id': {'title': 'Id', 'type': 'string'},\n",
       "    'example': {'title': 'Example', 'default': False, 'type': 'boolean'}},\n",
       "   'required': ['content']},\n",
       "  'ChatMessage': {'title': 'ChatMessage',\n",
       "   'description': 'Message that can be assigned an arbitrary speaker (i.e. role).',\n",
       "   'type': 'object',\n",
       "   'properties': {'content': {'title': 'Content',\n",
       "     'anyOf': [{'type': 'string'},\n",
       "      {'type': 'array',\n",
       "       'items': {'anyOf': [{'type': 'string'}, {'type': 'object'}]}}]},\n",
       "    'additional_kwargs': {'title': 'Additional Kwargs', 'type': 'object'},\n",
       "    'response_metadata': {'title': 'Response Metadata', 'type': 'object'},\n",
       "    'type': {'title': 'Type',\n",
       "     'default': 'chat',\n",
       "     'enum': ['chat'],\n",
       "     'type': 'string'},\n",
       "    'name': {'title': 'Name', 'type': 'string'},\n",
       "    'id': {'title': 'Id', 'type': 'string'},\n",
       "    'role': {'title': 'Role', 'type': 'string'}},\n",
       "   'required': ['content', 'role']},\n",
       "  'SystemMessage': {'title': 'SystemMessage',\n",
       "   'description': 'Message for priming AI behavior, usually passed in as the first of a sequence\\nof input messages.',\n",
       "   'type': 'object',\n",
       "   'properties': {'content': {'title': 'Content',\n",
       "     'anyOf': [{'type': 'string'},\n",
       "      {'type': 'array',\n",
       "       'items': {'anyOf': [{'type': 'string'}, {'type': 'object'}]}}]},\n",
       "    'additional_kwargs': {'title': 'Additional Kwargs', 'type': 'object'},\n",
       "    'response_metadata': {'title': 'Response Metadata', 'type': 'object'},\n",
       "    'type': {'title': 'Type',\n",
       "     'default': 'system',\n",
       "     'enum': ['system'],\n",
       "     'type': 'string'},\n",
       "    'name': {'title': 'Name', 'type': 'string'},\n",
       "    'id': {'title': 'Id', 'type': 'string'}},\n",
       "   'required': ['content']},\n",
       "  'FunctionMessage': {'title': 'FunctionMessage',\n",
       "   'description': 'Message for passing the result of executing a function back to a model.',\n",
       "   'type': 'object',\n",
       "   'properties': {'content': {'title': 'Content',\n",
       "     'anyOf': [{'type': 'string'},\n",
       "      {'type': 'array',\n",
       "       'items': {'anyOf': [{'type': 'string'}, {'type': 'object'}]}}]},\n",
       "    'additional_kwargs': {'title': 'Additional Kwargs', 'type': 'object'},\n",
       "    'response_metadata': {'title': 'Response Metadata', 'type': 'object'},\n",
       "    'type': {'title': 'Type',\n",
       "     'default': 'function',\n",
       "     'enum': ['function'],\n",
       "     'type': 'string'},\n",
       "    'name': {'title': 'Name', 'type': 'string'},\n",
       "    'id': {'title': 'Id', 'type': 'string'}},\n",
       "   'required': ['content', 'name']},\n",
       "  'ToolMessage': {'title': 'ToolMessage',\n",
       "   'description': 'Message for passing the result of executing a tool back to a model.',\n",
       "   'type': 'object',\n",
       "   'properties': {'content': {'title': 'Content',\n",
       "     'anyOf': [{'type': 'string'},\n",
       "      {'type': 'array',\n",
       "       'items': {'anyOf': [{'type': 'string'}, {'type': 'object'}]}}]},\n",
       "    'additional_kwargs': {'title': 'Additional Kwargs', 'type': 'object'},\n",
       "    'response_metadata': {'title': 'Response Metadata', 'type': 'object'},\n",
       "    'type': {'title': 'Type',\n",
       "     'default': 'tool',\n",
       "     'enum': ['tool'],\n",
       "     'type': 'string'},\n",
       "    'name': {'title': 'Name', 'type': 'string'},\n",
       "    'id': {'title': 'Id', 'type': 'string'},\n",
       "    'tool_call_id': {'title': 'Tool Call Id', 'type': 'string'}},\n",
       "   'required': ['content', 'tool_call_id']}}}"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 查看 Chain 的输出类型\n",
    "chain.output_schema.schema()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dc971bb2-8279-4e38-a539-54cc41a5e4a5",
   "metadata": {},
   "source": [
    "### Stream\n",
    "\n",
    "使用 .stream() 方法查看（同步）流式输出结果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "3cebbeb5-72e0-4606-8383-fa7efa6465bc",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "为什么程序员喜欢使用黑色主题的IDE？\n",
      "\n",
      "因为黑色主题省电，可以让他们多玩一会儿游戏！"
     ]
    }
   ],
   "source": [
    "for s in chain.stream({\"topic\": \"程序员\"}):\n",
    "    print(s.content, end=\"\", flush=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "444adacc-ee61-4c41-a2a0-c7a6278a6dea",
   "metadata": {},
   "source": [
    "### Invoke\n",
    "使用 .invoke() 方法单次（同步）调用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "f8b68201-63a0-48f0-ae40-a835fb889c12",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='为什么程序员总是搞不懂女朋友的心思？\\n\\n因为他们只擅长理解代码，而不是人类的情感。', response_metadata={'token_usage': {'completion_tokens': 45, 'prompt_tokens': 22, 'total_tokens': 67}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None})"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.invoke({\"topic\": \"程序员\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e16f0ec6-f2d6-4b75-ae6c-6e5e685a326b",
   "metadata": {},
   "source": [
    "### Batch\n",
    "使用 .batch() 方法（同步）批量调用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "867e73cd-e59e-47d3-8284-d0b97eddc5a9",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[AIMessage(content='为什么程序员总是在深夜工作？\\n因为他们喜欢在“黑暗中”发现bug！😄', response_metadata={'token_usage': {'completion_tokens': 37, 'prompt_tokens': 22, 'total_tokens': 59}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}),\n",
       " AIMessage(content='为什么产品经理总是迟到？\\n\\n因为他们总是在把需求变更加进他们的早间会议议程里！', response_metadata={'token_usage': {'completion_tokens': 40, 'prompt_tokens': 21, 'total_tokens': 61}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}),\n",
       " AIMessage(content='为什么测试经理总是在压力下笑得最开心？\\n\\n因为他知道只有通过了测试，他才能安心地笑出声来！', response_metadata={'token_usage': {'completion_tokens': 43, 'prompt_tokens': 21, 'total_tokens': 64}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None})]"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.batch([{\"topic\": \"程序员\"}, {\"topic\": \"产品经理\"}, {\"topic\": \"测试经理\"}])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "99c55997-250a-4603-985b-674f33a39bf9",
   "metadata": {},
   "outputs": [],
   "source": [
    "messages = chain.batch([{\"topic\": \"程序员\"}, {\"topic\": \"产品经理\"}, {\"topic\": \"测试经理\"}])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "7648e692-7581-4fc0-a453-52cb0e9484f4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "笑话0:\n",
      "\n",
      "为什么程序员喜欢使用黑色主题的编辑器？\n",
      "\n",
      "因为黑色背景可以减少眼睛的疲劳，让他们更专注地写代码！\n",
      "\n",
      "\n",
      "笑话1:\n",
      "\n",
      "为什么产品经理总是带着尺子去上班？\n",
      "\n",
      "因为他们要时刻确保产品的尺寸符合用户需求！\n",
      "\n",
      "\n",
      "笑话2:\n",
      "\n",
      "为什么测试经理总是喜欢把Bug藏得那么深？\n",
      "\n",
      "因为他们觉得Bug是个宝藏，要让开发人员找到它才能锻炼他们的能力！\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# 使用 StrOutputParser 来处理 Batch 批量输出\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "\n",
    "output_parser = StrOutputParser()\n",
    "\n",
    "for idx, m in enumerate(messages):\n",
    "    print(f\"笑话{idx}:\\n\")\n",
    "    print(output_parser.invoke(m))\n",
    "    print(\"\\n\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cc1d9980-0d5a-4fe7-9a5a-5c6a7fbdfafe",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "34e8436e-a647-4562-a2e2-efade07fa090",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "285e2800-354e-4575-9f1c-d2d2a275de3e",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "c0bfd2f7-586a-46f8-846a-2f0ea99be1d4",
   "metadata": {},
   "source": [
    "## 异步操作\n",
    "\n",
    "这些方法也有相应的异步方法，应与 `asyncio` 的 `await` 语法一起使用以进行并发操作：\n",
    "\n",
    "- astream：异步地流式返回生成内容（chunk）\n",
    "- ainvoke：异步地对输入调用该链\n",
    "- abatch：异步地对输入列表调用该链\n",
    "- astream_log: 在发生时会返回中间步骤，并且最终返回结果之外。\n",
    "- astream_events: beta 流式传输事件，在 langchain-core 0.1.14 中引入\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "85c2d21e-17c7-42e4-905a-9c7e924b44fc",
   "metadata": {},
   "source": [
    "### Async Stream"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "0cbd50d5-8706-4105-8191-41278004c57a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "为什么程序员喜欢看恐怖片？\n",
      "\n",
      "因为他们喜欢在代码中寻找bug！😂"
     ]
    }
   ],
   "source": [
    "async for s in chain.astream({\"topic\": \"程序员\"}):\n",
    "    print(s.content, end=\"\", flush=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4718496f-ce02-4465-8757-3f6d6e48c063",
   "metadata": {},
   "source": [
    "### Async Invoke"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "dc1fceaf-15aa-44f1-9f31-81d82d64f43a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='为什么程序员总是觉得暖和？\\n\\n因为他们总是在写bug！', response_metadata={'token_usage': {'completion_tokens': 25, 'prompt_tokens': 22, 'total_tokens': 47}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None})"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "await chain.ainvoke({\"topic\": \"程序员\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2b88f80e-0b9d-49b4-a44d-9f26f9da2819",
   "metadata": {},
   "source": [
    "### Async Batch"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "75f6493d-0b88-4584-89ac-63b60a21fb37",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[AIMessage(content='为什么程序员喜欢喝咖啡？\\n\\n因为咖啡是 Java！', response_metadata={'token_usage': {'completion_tokens': 28, 'prompt_tokens': 22, 'total_tokens': 50}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}),\n",
       " AIMessage(content='为什么产品经理总是带着尺子？\\n因为他们要不停地衡量用户需求！', response_metadata={'token_usage': {'completion_tokens': 32, 'prompt_tokens': 21, 'total_tokens': 53}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}),\n",
       " AIMessage(content='为什么测试经理总是笑得那么开心？\\n\\n因为他们喜欢看着bug被发现，然后一一解决，感觉就像在打小虫子一样爽！', response_metadata={'token_usage': {'completion_tokens': 58, 'prompt_tokens': 21, 'total_tokens': 79}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None})]"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "await chain.abatch([{\"topic\": \"程序员\"}, {\"topic\": \"产品经理\"}, {\"topic\": \"测试经理\"}])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "63e40e27-a304-45ae-bc58-1d759e823671",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
